package org.onionframework.faces.converter;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.ConverterException;
import javax.faces.convert.FacesConverter;

import org.apache.commons.collections4.BidiMap;
import org.apache.commons.collections4.bidimap.DualHashBidiMap;
import org.apache.commons.lang3.StringUtils;

/**
 * 实体转换器
 * 
 * 在响应客户端请求时，把对象放到组件的属性中，把顺序号作为客户端的值
 * 客户端提交表单后，根据选择，拿顺序号找到对应的实体
 * 
 * 在使用<h:selectOneMenu/>等标记的时候，使用本转换器，实际上客户端的
 * Options拿到的是一个顺序号，对应后端的实体，不再像以前那样把实体的id
 * 作为option的value
 * 
 * @author allan
 *
 */
@FacesConverter("onion.faces.EntityConverter")
public class EntityConverter implements Converter {

	private static final String CONTAINER_KEY = "onion.converter.entities";
	
	@Override
	public Object getAsObject(FacesContext context, UIComponent component, String value) {
		
		if(value == null || StringUtils.isBlank(value.toString()))
			return null;
		else{
			BidiMap<String,Object> container = this.getContainer(component);
			Object result = container.get(value);
			return result;
		}
	}
	
	/**
	 * 使用双向Map作为数据容器，因为需要从实体找到顺序号，也需要从顺序号找到实体
	 * 
	 * @param component
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private BidiMap<String,Object> getContainer(UIComponent component){
		BidiMap<String,Object> container = (BidiMap<String,Object>)component.getAttributes().get(CONTAINER_KEY);
		if(container == null){
			container = new DualHashBidiMap<String,Object>();
			component.getAttributes().put(CONTAINER_KEY, container);
		}
		return container;
	}

	@Override
	public String getAsString(FacesContext context, UIComponent component, Object value) {
		try {
			if(value == null)
				return null;
			
			BidiMap<String,Object> container = this.getContainer(component);
			
			String key = container.getKey(value);
			if(key == null){
				key = String.valueOf(container.size());
				container.put(key, value);
			}
			
			return String.valueOf(key);
			
		} catch (Exception e) {
			throw new ConverterException(e);
		}
	}

}
